#include "common.h"
#include <Windows.h>
#include <dokan.h>
#include <boost/program_options.hpp>
#include "cpfs.h"
#include "pickdev.h"

using std::string;

int main(int argc, char **argv)
{
    namespace po = boost::program_options;
    po::options_description optdesc;
    optdesc.add_options()
            ("offset", "offset to beginning of filesystem")
            ("length", "size of device filesystem is hosted on")
            ("path", "device containing the filesystem")
        ;
    po::positional_options_description posopts;
    posopts.add("path", 1);
    po::variables_map varmap;
    po::store(po::command_line_parser(argc, argv).options(optdesc).positional(posopts).run(), varmap);
    po::notify(varmap);
    Device device;
    if (varmap["path"].empty()) {
        if (!choose_device(device))
            return 1;
    } else {
        device.path = varmap["path"].as<string>();
        device.offset = -1;
        device.size = -1;
    }
    if (!varmap["offset"].empty())
        device.offset = varmap["offset"].as<int64_t>();
    if (!varmap["length"].empty())
        device.size = varmap["length"].as<int64_t>();
    Cpfs *cpfs = cpfs_load(device.path.c_str(), device.size, device.offset);
    if (!cpfs) {
        return 1;
    }

	DOKAN_OPTIONS dokanOptions = {};
	dokanOptions.DriveLetter = L'x';
	dokanOptions.ThreadCount = 1; // cpfs is single threaded
    dokanOptions.Options = 0;//DOKAN_OPTION_KEEP_ALIVE;
#ifdef DEBUG
	dokanOptions.Options |= DOKAN_OPTION_DEBUG | DOKAN_OPTION_STDERR;
#endif
    {
        CpfsDokanGlobalContext *cdgc = new CpfsDokanGlobalContext();
        cdgc->cpfsInstance = cpfs;
        cdgc->volumeName = argv[1];
        dokanOptions.GlobalContext = (uintptr_t)cdgc;
    }

	int dokanStatus = DokanMain(&dokanOptions, &cpfsDokanOps);
    if (dokanOptions.GlobalContext) {
        delete (CpfsDokanGlobalContext *)dokanOptions.GlobalContext;
        dokanOptions.GlobalContext = 0;
    }
	switch (dokanStatus)
    {
	case DOKAN_SUCCESS:
		fprintf(stderr, "Success\n");
		break;
	case DOKAN_ERROR:
		fprintf(stderr, "Error\n");
		break;
	case DOKAN_DRIVE_LETTER_ERROR:
		fprintf(stderr, "Bad Drive letter\n");
		break;
	case DOKAN_DRIVER_INSTALL_ERROR:
		fprintf(stderr, "Can't install driver\n");
		break;
	case DOKAN_START_ERROR:
		fprintf(stderr, "Driver something wrong\n");
		break;
	case DOKAN_MOUNT_ERROR:
		fprintf(stderr, "Can't assign a drive letter\n");
		break;
	default:
		fprintf(stderr, "Unknown error: %d\n", dokanStatus);
		break;
	}

    return dokanStatus;
}
